using Rogan.Data.Extensions;
using Rogan.Data.Resources;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Text;
using System.Text.RegularExpressions;
using Rogan.Utility.Common;

namespace Rogan.Data
{
	public abstract class DbProvider
	{
		internal static readonly string SQL_PAGING = " SELECT * FROM (SELECT TOP {M_N_1} * FROM ({OLDSQL} {OLDORDER}) AS tmptableinner {REVORDER}) AS tmptableouter {ORDER} ";
		internal static readonly Regex REGEX_ISNULL = new Regex("isnull\\((.*?),(.*?)\\)", RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);
		protected char leftToken;
		protected char paramPrefixToken;
		protected char rightToken;
		protected DbProviderFactory dbProviderFactory;
		protected DbConnectionStringBuilder dbConnStrBuilder;
		public string ConnectionStringsName
		{
			get;
			set;
		}
		public string ConnectionString
		{
			get
			{
				return dbConnStrBuilder.ConnectionString;
			}
		}
		public DbProviderFactory DbProviderFactory
		{
			get
			{
				return dbProviderFactory;
			}
		}
		public char ParamPrefix
		{
			get
			{
				return paramPrefixToken;
			}
		}
		public char LeftToken
		{
			get
			{
				return leftToken;
			}
		}
		public char RightToken
		{
			get
			{
				return rightToken;
			}
		}
		internal DbCacheConfiguration CacheConfig
		{
			get;
			set;
		}
		internal Dictionary<string, CacheInfo> EntitiesCache
		{
			get;
			set;
		}
		public abstract string RowAutoID
		{
			get;
		}
		public abstract bool SupportBatch
		{
			get;
		}
		protected DbProvider(string connectionString, DbProviderFactory dbProviderFactory, char leftToken, char rightToken, char paramPrefixToken)
		{
			dbConnStrBuilder = new DbConnectionStringBuilder();
			dbConnStrBuilder.ConnectionString = connectionString;
			this.dbProviderFactory = dbProviderFactory;
			this.leftToken = leftToken;
			this.rightToken = rightToken;
			this.paramPrefixToken = paramPrefixToken;
			CacheConfig = new DbCacheConfiguration();
			EntitiesCache = new Dictionary<string, CacheInfo>();
		}
		public virtual string BuildTableName(string name)
		{
			return leftToken.ToString() + name.Trim(new[]
			{
				leftToken,
				rightToken
			}) + rightToken.ToString();
		}
		public virtual string BuildParameterName(string name)
		{
			string text = name.Trim(new[]
			{
				leftToken,
				rightToken
			});
			if (text[0] != paramPrefixToken)
			{
				return paramPrefixToken + text;
			}
			return text;
		}
		public virtual void PrepareCommand(DbCommand cmd)
		{
			cmd.CommandText = DataHelper.FormatSql(cmd.CommandText, leftToken, rightToken);
			foreach (DbParameter dbParameter in cmd.Parameters)
			{
				if (cmd.CommandText.IndexOf(dbParameter.ParameterName) == -1)
				{
					cmd.CommandText = cmd.CommandText.Replace(dbParameter.ParameterName.Substring(1), dbParameter.ParameterName);
				}
				if (dbParameter.Direction != ParameterDirection.Output && dbParameter.Direction != ParameterDirection.ReturnValue)
				{
					object value = dbParameter.Value;
					DbType dbType = dbParameter.DbType;
					if (value != DBNull.Value && value != null)
					{
						Type type = value.GetType();
						if (type.IsEnum)
						{
							dbParameter.DbType = DbType.Int32;
							dbParameter.Value = Convert.ToInt32(value);
						}
						else
						{
							if (TypeHelper.IsInteger(type))
							{
								dbParameter.DbType = DbType.Int32;
								dbParameter.Value = Convert.ToInt32(value);
							}
							else
							{
								if (dbType == DbType.Guid && type != typeof(Guid))
								{
									dbParameter.Value = new Guid(value.ToString());
								}
								else
								{
									if (type == typeof(bool))
									{
										dbParameter.Value = (((bool)value) ? 1 : 0);
									}
								}
							}
						}
					}
				}
			}
		}
		public virtual DbCommand CreateCommand(string commandText, QueryParameter[] Parms)
		{
			DbCommand dbCommand = dbProviderFactory.CreateCommand();
			dbCommand.CommandText = commandText;
			if (Parms != null)
			{
				for (int i = 0; i < Parms.Length; i++)
				{
					QueryParameter queryParameter = Parms[i];
					DbParameter dbParameter = DbProviderFactory.CreateParameter();
					dbParameter.ParameterName = BuildParameterName(queryParameter.Name);
					dbParameter.Value = queryParameter.Value;
					if (queryParameter.DbType.HasValue)
					{
						dbParameter.DbType = queryParameter.DbType.Value;
					}
					if (queryParameter.Size.HasValue)
					{
						dbParameter.Size = queryParameter.Size.Value;
					}
					dbCommand.Parameters.Add(dbParameter);
				}
			}
			return dbCommand;
		}
		internal ExpressionTranslater CreateTranslater(Entity table)
		{
			return new ExpressionTranslater(this, new EntityQueryExpression(table));
		}
		public virtual void PrepareTranslater(ExpressionTranslater translater)
		{
			EntityQueryExpression expression = translater.Expression;
			if (expression.IsPagable)
			{
				expression.IsDistinct = new bool?(false);
				expression.GroupBy = null;
				expression.IsOrderBy = false;
				return;
			}
			if (expression.IsSkip)
			{
				PrepareSkip(translater);
			}
		}
		protected virtual void PrepareSkip(ExpressionTranslater translater)
		{
			EntityQueryExpression expression = translater.Expression;
			Field identityField = expression.From.GetIdentityField();
			if (identityField == null)
			{
				throw new NotImplementedException(TextResource.AutoKeyToSearchAllow);
			}
			bool isIdentityFieldDesc = expression.IsIdentityFieldDesc;
			string text = "subtable";
			Field field = identityField.SetTableName(text);
			Entity entity = DataHelper.Clone(expression.From) as Entity;
			entity.SetAliasName(text);
			EntityQueryExpression entityQueryExpression = new EntityQueryExpression(entity);
			if (isIdentityFieldDesc)
			{
				entityQueryExpression.Select = new SelectExpression(new[]
				{
					field.Min()
				});
			}
			else
			{
				entityQueryExpression.Select = new SelectExpression(new[]
				{
					field.Max()
				});
			}
			entityQueryExpression.Where = expression.Where.Clone();
			entityQueryExpression.Where.SetTable(text);
			entityQueryExpression.OrderByList = expression.OrderByList.Clone().SetTable(text);
			WhereExpression right = WhereExpression.Create(expression.From.GetIdentityField(), entityQueryExpression, isIdentityFieldDesc ? QueryOperator.Less : QueryOperator.Greater);
			expression.Where = WhereExpression.Create(expression.Where, right, QueryOperator.And);
		}
		public virtual string GetSQLSelectTopClip(ExpressionTranslater translater)
		{
			StringBuilder stringBuilder = new StringBuilder();
			EntityQueryExpression expression = translater.Expression;
			if (expression.IsPagable)
			{
				stringBuilder.Append(" TOP " + expression.EndIndex + " ");
			}
			else
			{
				if (expression.IsTop)
				{
					stringBuilder.Append(" TOP " + expression.EndIndex + " ");
				}
			}
			return stringBuilder.ToString();
		}
		public virtual void AdjustTranslater(ExpressionTranslater translater)
		{
			EntityQueryExpression expression = translater.Expression;
			if (expression.IsPagable)
			{
				AdjustPagable(translater);
			}
		}
		protected virtual void AdjustPagable(ExpressionTranslater translater)
		{
			string commandText = translater.CommandText;
			EntityQueryExpression expression = translater.Expression;
			string text = SQL_PAGING.ToString();
			IList<OrderByExpression> orderbys = expression.IsOrderBy ? expression.OrderByList : new List<OrderByExpression>
			{
				new OrderByExpression(expression.From.GetIdentityField(), OrderByDirection.Asc)
			};
			text = text.Replace("{M_N_1}", (expression.EndIndex.Value - expression.StartIndex.Value + 1).ToString());
			text = text.Replace("{OLDORDER}", expression.IsOrderBy ? "" : translater.TranslateOrderyByList(orderbys, false));
			text = text.Replace("{REVORDER}", translater.TranslateOrderyByList(orderbys, true));
			text = text.Replace("{ORDER}", translater.TranslateOrderyByList(orderbys, false));
			text = text.Replace("{OLDSQL}", commandText);
			translater.SetCommandText(text);
		}
	}
}
